package com.google.jessewilson.codesearch.ui.tree;

import com.google.jessewilson.codesearch.api.CodeFile;
import com.google.jessewilson.codesearch.api.ContentRoot;
import com.google.jessewilson.codesearch.io.Path;

import java.awt.*;

import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

/**
 * Render a search result so that it looks like IntelliJ's Find Usages dialog.
 *
 * @author jessewilson
 */
final class ElementRenderer extends JPanel implements TableCellRenderer {

  private static final Color FILENAME_GREEN = new Color(10, 119, 0);
  private static final Color FILEPATH_GREEN = new Color(129, 196, 110);
  private static final Color FILENAME_BLUE = new Color(0, 0, 128);
  private static final Color FILEPATH_BLUE = new Color(142, 147, 216);
  private static final Color KEYWORD_BLUE = new Color(0, 0, 128);
  private static final Color LINE_NUMBER_GREY = new Color(128, 128, 128);
  private static final Color BACKGROUND = UIManager.getColor("TextField.background");
  private static final Color SELECTED_BACKGROUND = UIManager.getColor("TextField.selectionBackground");

  private JTextPane rendered;

  private DefaultStyledDocument document;
  private Style regular;

  private Style language;
  private Style usagesInLanguageCount;

  private Style localFileName;
  private Style localFilePath;
  private Style remoteFileName;
  private Style remoteFilePath;
  private Style usagesInFileCount;

  private Style lineNumber;
  private Style keyword;
  private Style matchText;

  public ElementRenderer() {
    super(new BorderLayout());

    setBackground(Color.WHITE);

    document = new DefaultStyledDocument();
    rendered = new JTextPane(document) {
      public void setBounds(int x, int y, int width, int height) {
        width = Math.max(width, getPreferredSize().width);
        super.setBounds(x, y, width, height);
      }
    };
    rendered.setMargin(new Insets(0, 0, 0, 0));
    add(rendered, BorderLayout.WEST);
    add(Box.createGlue(), BorderLayout.CENTER);

    Style defaultStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
    StyleConstants.setForeground(defaultStyle, Color.BLACK);

    regular = document.addStyle("regular", defaultStyle);
    StyleConstants.setFontFamily(defaultStyle, "SansSerif");

    language = document.addStyle("language", regular);
    StyleConstants.setBold(language, true);

    usagesInLanguageCount = document.addStyle("usagesInLanguageCount", regular);
    StyleConstants.setBold(usagesInLanguageCount, true);
    StyleConstants.setItalic(usagesInLanguageCount, true);
    StyleConstants.setForeground(usagesInLanguageCount, LINE_NUMBER_GREY);

    localFileName = document.addStyle("localFileName", regular);
    StyleConstants.setForeground(localFileName, FILENAME_GREEN);

    localFilePath = document.addStyle("localFilePath", localFileName);
    StyleConstants.setForeground(localFilePath, FILEPATH_GREEN);
    StyleConstants.setItalic(localFilePath, true);

    remoteFileName = document.addStyle("remoteFileName", regular);
    StyleConstants.setForeground(remoteFileName, FILENAME_BLUE);

    remoteFilePath = document.addStyle("remoteFilePath", localFileName);
    StyleConstants.setForeground(remoteFilePath, FILEPATH_BLUE);
    StyleConstants.setItalic(remoteFilePath, true);

    usagesInFileCount = document.addStyle("usagesInFileCount", localFileName);
    StyleConstants.setItalic(usagesInFileCount, true);

    lineNumber = document.addStyle("lineNumber", regular);
    StyleConstants.setItalic(lineNumber, true);
    StyleConstants.setForeground(lineNumber, LINE_NUMBER_GREY);

    keyword = document.addStyle("keyword", regular);
    StyleConstants.setForeground(keyword, KEYWORD_BLUE);

    matchText = document.addStyle("matchText", regular);
    StyleConstants.setBold(matchText, true);
  }

  public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {

    clear();

    if(isSelected) {
      rendered.setBackground(SELECTED_BACKGROUND);
    } else {
      rendered.setBackground(BACKGROUND);
    }

    Element element = (Element) value;

    if(element.getType() == Type.LANGUAGE) {
      renderLanguage(element);

    } else if(element.getType() == Type.CONTENT_ROOT) {
      renderContentRoot(element);

    } else if(element.getType() == Type.FILE) {
      renderCodeFile(element);

    } else if(element.getType() == Type.LINE) {
      renderLine(element);

    } else {
      throw new IllegalStateException();
    }

    // because italic characters report the wrong width
    append(" ", regular);

    // so that everything is left justified
    rendered.setCaretPosition(0);

    return this;
  }

  private void renderLanguage(Element element) {
    append(element.getProgrammingLanguage().getName(), language);
    //append(" (??)", usagesInLanguageCount);
  }

  private void renderContentRoot(Element element) {
    ContentRoot contentRoot = element.getContentRoot();
    String contentName = contentRoot.getContentName();
    String contentPath = contentRoot.getContentPath();
    if(contentName != null) {
      append(contentName, remoteFileName);
      if(contentPath != null) {
        append(" at ", remoteFilePath);
        append(contentPath, remoteFilePath);
      }
    } else if(contentPath != null) {
      append(contentPath, remoteFileName);
    }
    //append(" (??)", usagesInContentRootcount);
  }

  private void renderCodeFile(Element element) {
    CodeFile codeFile = element.getFile();

    // colour the file differently if it's available locally
    boolean isLocal = codeFile.isAvailableLocally();
    Style filenameStyle = isLocal ? localFileName : remoteFileName;
    Style filePathStyle = isLocal ? localFilePath : remoteFilePath;

    append(codeFile.getPath().getFileName(), filenameStyle);
    Path parent = element.getFile().getPath().getParent();
    if(parent != null) {
      append(" in ", filePathStyle);
      append(parent.toString(), filePathStyle);
    }
    //append(" (??)", usagesInFileCount);
  }

  private void renderLine(Element element) {
    append("(" + (element.getLine().getLineNumber() + 1) + ")  ", lineNumber);
    append(element.getLine().getLineValue().trim(), regular);
  }

  protected void append(String text, Style style) {
    try {
      document.insertString(document.getLength(), text, style);
    } catch(BadLocationException unexpected) {
      throw new RuntimeException(unexpected);
    }
  }

  protected void clear() {
    try {
      document.remove(0, document.getLength());
    } catch(BadLocationException unexpected) {
      throw new RuntimeException(unexpected);
    }
  }

}
